package com.ncr.travel.appmgr.service.config;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.ncr.travel.tas.configuration.exceptions.TableCellException;
import com.ncr.travel.tas.configuration.model.ConfigurationValue;
import com.ncr.travel.tas.configuration.model.TableValue;
import com.ncr.travel.tas.configuration.service.messaging.ConfigurationService;
import com.ncr.travel.tas.configuration.service.messaging.ConfigurationValueCriteria;
import com.ncr.travel.tas.shared.configuration.ConfigurationKey;
import com.ncr.travel.tas.shared.configuration.ConfigurationNodeEnum;
import com.ncr.travel.tas.shared.configuration.ConfigurationProductKey;
import com.ncr.travel.tas.shared.configuration.ConfigurationServiceException;

@Component
@Transactional(readOnly = true)
public class ConfigurationCacheProvider implements ConfigurationCache {
	private static final Logger logger = LoggerFactory.getLogger(ConfigurationCacheProvider.class);

	private final ConfigurationService configurationService;

	private Set<ConfigurationValue> configValues = Collections.synchronizedSet(new HashSet<ConfigurationValue>());

	@Autowired
	public ConfigurationCacheProvider(ConfigurationService configurationService) {
		super();
		this.configurationService = configurationService;
	}

	@Override
	public Set<ConfigurationValue> getConfigurationValues(ConfigurationNodeEnum node, ConfigurationProductKey productKey, ConfigurationKey configKey) throws ConfigurationServiceException {

		ConfigurationValueCriteria criteria = new ConfigurationValueCriteria(node, productKey, configKey);

		if (configValues.isEmpty()) {
			configValues.addAll(configurationService.findConfiguration(criteria).get(0).getConfigurations());
		}
		// Set<ConfigurationValue> configValues =
		// configurationService.findConfiguration(criteria).get(0).getConfigurations();
		if (logger.isDebugEnabled()) {
			for (ConfigurationValue value : configValues) {
				logger.debug("Cached configurationValue = " + value);
				if (value.getChildValues() != null) {
					for (ConfigurationValue child : value.getChildValues()) {
						logger.debug("Cached child configurationValue = " + child);
						if (child.getChildValues() != null) {
							for (ConfigurationValue subChild : child.getChildValues()) {
								logger.debug("Cached subChild configurationValue = " + subChild);
								if (subChild.getChildValues() != null) {
									for (ConfigurationValue subSubChild : subChild.getChildValues()) {
										logger.debug("Cached subSubChild configurationValue = " + subSubChild);
										if (subSubChild.getChildValues() != null) {
											for (ConfigurationValue subSubSubChild : subSubChild.getChildValues()) {
												logger.debug("Cached subSubSubChild configurationValue = " + subSubSubChild);
												if (subSubSubChild.getChildValues() != null) {
													for (ConfigurationValue subSubSubSubChild : subSubSubChild.getChildValues()) {
														logger.debug("Cached subSubSubSubChild configurationValue = " + subSubSubSubChild);
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}

		return configValues;
	}

	@Override
	@Transactional
	public void setConfigurationValue(ConfigurationValue configurationValue, ConfigurationKey key) {
		ConfigurationValue origConfigValue = null;
		for (Iterator<ConfigurationValue> it = configValues.iterator(); it.hasNext();) {
			ConfigurationValue configValue = it.next();
			if (configurationValue.getKeyName().equals(configValue.getKeyName())) {
				origConfigValue = configValue;
				break;
			}
			origConfigValue = findConfigurationValue(configurationValue.getKeyName(), configValue);
			if (origConfigValue != null) {
				break;
			}
		}
		if (origConfigValue != null) {
			if (configurationValue.getValue() != null) {
				origConfigValue.setValue(configurationValue.getValue());
				configurationService.updateConfigurationValue(origConfigValue);
			}
			// Is this a rule?
			if (key == ConfigurationKey.RULES) {
				for (ConfigurationValue ruleConfigValue : configurationValue.getChildValues()) {
					if (ruleConfigValue.getObjectType() != null && ruleConfigValue.getObjectType().equals("TableValue")) {
						TableValue ruleTableValue = (TableValue) ruleConfigValue;
						origConfigValue = findConfigurationValue(ruleTableValue.getKeyName(), origConfigValue);
						if (origConfigValue.getObjectType() != null && origConfigValue.getObjectType().equals("TableValue")) {
							TableValue origRuleTableValue = null;
							if (origConfigValue instanceof TableValue) {
								origRuleTableValue = (TableValue) origConfigValue;
							} else {
								origRuleTableValue = new TableValue(origConfigValue);
							}
							for (int row = 0; row < origRuleTableValue.getTableRows().size(); row++) {
								for (int col = 1; col < origRuleTableValue.getTableColumns().size(); col++) {
									try {
										ConfigurationValue cell = origRuleTableValue.getCell(row, col);
										cell.setValue(ruleTableValue.getCell(row, col).getValue());
									} catch (TableCellException e) {
										throw new ConfigurationServiceException(e.getMessage(), e);
									}
								}
							}
							origRuleTableValue.setTableColumns(ruleTableValue.getTableColumns());
							origRuleTableValue.setTableRows(ruleTableValue.getTableRows());
							configurationService.updateConfigurationValue(origConfigValue);
						}
					}
				}
			}
		} else {
			throw new ConfigurationServiceException("ConfigurationValue not found with key = '" + configurationValue.getKeyName() + "'", null);
		}
	}

	private ConfigurationValue findConfigurationValue(String searchKey, ConfigurationValue configurationValue) {
		for (Iterator<ConfigurationValue> it = configurationValue.getChildValues().iterator(); it.hasNext();) {
			ConfigurationValue configValue = it.next();
			if (searchKey.equals(configValue.getKeyName())) {
				return configValue;
			}
			ConfigurationValue found = findConfigurationValue(searchKey, configValue);
			if (found != null) {
				return found;
			}
		}
		return null;
	}
}
